package br.sales.persistence;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;

import br.sales.entity.DynamicQuery;
import br.sales.persistence.exception.DBContainerNotInitializedException;

import com.db4o.ObjectContainer;
import com.db4o.ObjectSet;
import com.db4o.ext.DatabaseClosedException;
import com.db4o.ext.DatabaseReadOnlyException;
import com.db4o.ext.Db4oIOException;
import com.db4o.query.Constraint;
import com.db4o.query.Query;

@SuppressWarnings("rawtypes")
public class DB4ODAO implements IPersistence{

	
	private ObjectContainer db4oContainer; 

	
	public DB4ODAO(ObjectContainer session) {
		db4oContainer = session;
	}


	@Override
	public Object save(Object o) throws DBContainerNotInitializedException {
		if (db4oContainer != null){
			db4oContainer.store(o);
			return retrieveByExample(o);
		}else{
			throw new DBContainerNotInitializedException();
		}
	}
	

	@Override
	public Object update(Object o) throws DBContainerNotInitializedException {
			return save(o);
	}


	@Override
	public Object retrieve(Object primaryKey, Class clazz)
			throws DBContainerNotInitializedException {
		if (db4oContainer != null){
			return null;
		}else{
			throw new DBContainerNotInitializedException();
		}
	}

	@Override
	public Object retrieveByExample(Object o)
			throws DBContainerNotInitializedException {
		if (db4oContainer != null){
			try{
				ObjectSet toReturn = db4oContainer.queryByExample(o);
				return toReturn.next();
			}catch (Exception e) {
				e.printStackTrace();
				return null;
			}
		}else{
			throw new DBContainerNotInitializedException();
		}
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public List retrieveAll(Class clazz)
			throws DBContainerNotInitializedException {
		if (db4oContainer != null){
			List list = new ArrayList();
			list.addAll(db4oContainer.queryByExample(clazz));
			return list;
		} else{
			throw new DBContainerNotInitializedException();
		}
	}
	
	@Override
	public boolean delete(Object object)
			throws DBContainerNotInitializedException {
		if (db4oContainer != null){
			db4oContainer.delete(object);
			return true;
		}else{
			throw new DBContainerNotInitializedException();
		}
	}


	@Override
	public void commit() throws DBContainerNotInitializedException, Db4oIOException, DatabaseClosedException, DatabaseReadOnlyException {
		if (db4oContainer != null){
			try {
				db4oContainer.commit();
			}catch ( Db4oIOException e){
				e.printStackTrace();
				rollback();
				throw e;
			} catch (DatabaseClosedException e){
				e.printStackTrace();
				rollback();
				throw e;
			}catch (DatabaseReadOnlyException e){
				e.printStackTrace();
				rollback();
				throw e;
			}
		}else{
			throw new DBContainerNotInitializedException();
		}
	}


	@Override
	public void rollback() throws DBContainerNotInitializedException {
		if (db4oContainer != null){
			db4oContainer.rollback();
		}else{
			throw new DBContainerNotInitializedException();
		}
	}

	@Override
	public void close() throws DBContainerNotInitializedException, Db4oIOException {
		if (db4oContainer != null){
			try{
				db4oContainer.close();
			}catch (Db4oIOException e) {
				rollback();
				e.printStackTrace();
				throw e;
			}
		}else{
			throw new DBContainerNotInitializedException();
		}
	}

	
	@Override
	public ObjectContainer getContainer(){
		return db4oContainer != null ? db4oContainer : null ;
	}
	
	
	@SuppressWarnings("unchecked")
	public List createNativeQuery(List<DynamicQuery> dqs , Class clazz) throws DBContainerNotInitializedException{
		
		if (db4oContainer == null){
			throw new DBContainerNotInitializedException();
		}
		
		Query q = db4oContainer.query();
		q.constrain(clazz);
		Constraint c = null;
		for (DynamicQuery dq : dqs){
				Constraint dynamic = null;
			if (dq.getType() == DynamicQuery.LIKE){
				
				if (!dq.isNot()){
					dynamic = q.descend(dq.getDescend()).constrain(dq.getConstrain()).like();
				}else {
					dynamic = q.descend(dq.getDescend()).constrain(dq.getConstrain()).like().not();
				}
				
			} else if (dq.getType() == DynamicQuery.ENDSWITH){
			
				if (dq.isNot()){
					dynamic = q.descend(dq.getDescend()).constrain(dq.getConstrain()).endsWith(false);
				}else {
					dynamic = q.descend(dq.getDescend()).constrain(dq.getConstrain()).endsWith(false).not();
				}
				
			} else if (dq.getType() == DynamicQuery.SMALLER){
				
				if (!dq.isNot()){
					dynamic = q.descend(dq.getDescend()).constrain(dq.getConstrain()).smaller();
				}else {
					dynamic = q.descend(dq.getDescend()).constrain(dq.getConstrain()).smaller().not();
				}
				
			} else if (dq.getType() == DynamicQuery.GREATER){

				if (!dq.isNot()){
					dynamic = q.descend(dq.getDescend()).constrain(dq.getConstrain()).greater();
				}else {
					dynamic = q.descend(dq.getDescend()).constrain(dq.getConstrain()).greater().not();
				}
				
			} else if (dq.getType() == DynamicQuery.EQUAL){
				
				if (!dq.isNot()){
					dynamic = q.descend(dq.getDescend()).constrain(dq.getConstrain()).equal();
				}else {
					dynamic = q.descend(dq.getDescend()).constrain(dq.getConstrain()).equal().not();
				}
				
			} else if (dq.getType() == DynamicQuery.STARTSWITH){
				
				if (!dq.isNot()){
					dynamic = q.descend(dq.getDescend()).constrain(dq.getConstrain()).startsWith(false);
				}else {
					dynamic = q.descend(dq.getDescend()).constrain(dq.getConstrain()).startsWith(false).not();
				}
				
			} else if (dq.getType() == DynamicQuery.CONTAINS){
				
				if (!dq.isNot()){
					dynamic = q.descend(dq.getDescend()).constrain(dq.getConstrain()).contains();
				}else {
					dynamic = q.descend(dq.getDescend()).constrain(dq.getConstrain()).contains().not();
				}
				
			}
			
			if (dynamic != null ){
			 c = checkOrOrAnd(dq, c, dynamic);
			}
			
		}
		
		
		
		List list = new ArrayList();
		list.addAll(q.execute());
		return list;
	}
	
	public Constraint checkOrOrAnd(DynamicQuery dq, Constraint c, Constraint dynamic){
		if (dq != null){
			if (dq.getOrOrAnd() != DynamicQuery.NONE && c != null){
				if (dq.getOrOrAnd() == DynamicQuery.OR){
						c = dynamic.or(c);
				} else {
					c =dynamic.and(c);
				}
			} else {
				c = dynamic;
			}
		}
		return c;
	}
	
	public long getObjectId(Object o){
		return ((ObjectContainer) getContainer()).ext().getID(o);
	}


}
